#pragma once

#include <iostream>
#include <map>
#include <string>

namespace flyweight {
    
/// @brief 非享元类, 以参数的形式注入具体享元的相关方法中
class UnsharableConcreteFlyweight {
public:
    UnsharableConcreteFlyweight(const std::string& content) : content_(content) {}
    const std::string& GetContent() const { return content_; }

private:
    std::string content_;
};

/// @brief 抽象享元类, 为具体享元规范需要实现的公共接口
class AbstractFlyweight {
public:
    virtual ~AbstractFlyweight() {}
    virtual void Run(UnsharableConcreteFlyweight* unsharable_content_ptr) = 0;
};

/// @brief 具体享元类, 实现抽象享元角色中所规定的接口
class ConcreteFlyweightA : public AbstractFlyweight {
public:
    virtual void Run(UnsharableConcreteFlyweight* unsharable_content_ptr) {
        std::cout << "ConcreteFlyweightA::Run!" << std::endl;
        std::cout << unsharable_content_ptr->GetContent() << std::endl;
    }
};
class ConcreteFlyweightB : public AbstractFlyweight {
public:
    virtual void Run(UnsharableConcreteFlyweight* unsharable_content_ptr) {
        std::cout << "ConcreteFlyweightB::Run!" << std::endl;
        std::cout << unsharable_content_ptr->GetContent() << std::endl;
    }
};

/// @brief 享元工厂类
class FlyweightFactory {
public:
    enum FlyweightType {
        ConcreteA = 0,
        ConcreteB,
    };

    ~FlyweightFactory() {
        for (auto it = fly_weights_.begin(); it != fly_weights_.end(); it++) {
            if (it->second) delete it->second;
        }
        fly_weights_.clear();
    }

    AbstractFlyweight* GetFlyweight(const std::string& key, const FlyweightType& type = ConcreteA) {
        auto it = fly_weights_.find(key);
        if (fly_weights_.end() == it) {
            AbstractFlyweight* ptr = nullptr;
            if (ConcreteA == type) {
                ptr = new ConcreteFlyweightA();
            }
            else if (ConcreteB == type) {
                ptr = new ConcreteFlyweightB();
            }
            fly_weights_[key] = ptr;
        }
        return fly_weights_[key];
    }

private:
    std::map<std::string, AbstractFlyweight*> fly_weights_;
};

} // namespace flyweight

void Test_11_flyweight_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<flyweight::UnsharableConcreteFlyweight> unsharable_content_ptr_1(new flyweight::UnsharableConcreteFlyweight("unsharable_content_ptr_1"));
    std::shared_ptr<flyweight::UnsharableConcreteFlyweight> unsharable_content_ptr_2(new flyweight::UnsharableConcreteFlyweight("unsharable_content_ptr_2"));

    std::shared_ptr<flyweight::FlyweightFactory> ptr(new flyweight::FlyweightFactory());
    flyweight::AbstractFlyweight* flyweight_ptr_A_red = ptr->GetFlyweight("Red");
    flyweight::AbstractFlyweight* flyweight_ptr_A_point = ptr->GetFlyweight("Point");
    
    flyweight_ptr_A_red->Run(unsharable_content_ptr_1.get());
    flyweight_ptr_A_point->Run(unsharable_content_ptr_2.get());
}

// -------------------- Test_11_flyweight_impl_1 --------------------
// ConcreteFlyweightA::Run!
// unsharable_content_ptr_1
// ConcreteFlyweightA::Run!
// unsharable_content_ptr_2